package com.ethan.blog.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ethan.blog.common.annotation.AutoFill;
import com.ethan.blog.common.enumeration.OperationType;
import com.ethan.blog.common.utils.ThreadLocalUtil;
import com.ethan.blog.mapper.UserMapper;
import com.ethan.blog.pojo.entity.User;
import com.ethan.blog.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;

/**
 * 用户表;(users)表服务实现类
 *
 * @author : Ethan
 * @date : 2024-5-24
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private UserMapper userMapper;

    /**
     * 指定账号、电话、或者邮箱查询用户
     * @param SFunctionType 获取账号、电话、或者邮箱的函数
     * @param account 账号、电话、或者邮箱
     * @return 返回用户信息，如果查询不到，返回空
     */
    @Override
    public <T> User getByAll(SFunction<User, T> SFunctionType, T account) {
        return lambdaQuery()
                .eq(SFunctionType, account)
                .list()
                .stream()
                .findFirst() // 获取第一个元素
                .orElse(null); // 如果元素为空，返回null
    }

    /**
     * 增加用户
     *
     * @param user
     */
    @AutoFill(OperationType.INSERT)
    /**
     * keypoint:
     *  之所以必须在实现类上加切面注解,是因为 Spring AOP 是基于代理模式实现的,它需要生成代理对象来拦截目标方法的调用并应用切面逻辑。
     *  而接口本身没有可拦截的方法实现,所以无法为接口生成代理对象。
     *  我们再仔细分析一下这个情况:
     *  在 Spring 的 MyBatis 集成中,MyBatis 会为我们自定义的 Mapper 接口生成动态代理实现类。这些动态代理类会实现我们定义的 Mapper 接口。
     *  当 Spring AOP 检测到 Mapper 接口上有切面注解时,它会为这些动态代理类生成代理对象,从而能够拦截接口方法的调用并应用切面逻辑。
     *  这与我之前说的 Spring AOP 只能为实现类生成代理不太一样。因为 MyBatis 提供了动态代理的功能,Spring AOP 可以利用这一点来代理 Mapper 接口。
     *  所以你说得对,在自定义的 Mapper 接口上应用切面注解是可以生效的。这是因为 MyBatis 提供了动态代理的支持,Spring AOP 可以利用这一特性来为接口生成代理对象。
     */
    @Override
    public Boolean insertUser(User user) {
        return save(user);
    }

    /**
     * 通过账号、手机号或邮箱查询用户
     *
     * @param accountOrPhoneNumberOrEmail
     * @return 用户信息
     */
    @Override
    public User getByAll(String accountOrPhoneNumberOrEmail) {
        return userMapper.getByAccountOrPhoneOrEmail(accountOrPhoneNumberOrEmail);
    }

    /**
     * 更新用户信息
     *
     * @param user
     * @return 返回查询状态
     */
    @Override
    public boolean updateUser(User user) {
        // keypoint：存入redis的Long属性，取出后会被转化为Integer，需要转换回Long
        Map<String, Object> claims = ThreadLocalUtil.get();
        Long byId = ((Integer) claims.get("id")).longValue();
        return lambdaUpdate()
                .set(User::getUpdateId, byId)
                .set(User::getUpdateTime, LocalDateTimeUtil.now())
                .eq(User::getAccount, user.getAccount())
                .update(user);
    }

    @Override
    public Boolean delete(Long id) {
        return removeById(id);
    }

    /**
     * 通过id修改密码
     *
     * @param id
     * @param newPassword
     * @return 是否修改成功
     */
    @Override
    public boolean updatePwd(Long id, String newPassword) {
        return lambdaUpdate()
                .set(User::getPassword, newPassword)
                .eq(User::getId, id)
                .update();
    }
}